#pragma once

#include "../mq_comm/mq_helper.hpp"
#include "../mq_comm/mq_log.hpp"
#include "../mq_comm/mq_msg.pb.h"
#include <vector>
#include <string>

namespace mq
{
    using namespace ns_helper;
    class Router
    {
    public:
        // 判断routing_key是否合法
        // a ~ z, A ~ Z, 0 ~ 9, _, .
        static bool isLegalRoutingKey(const std::string &routing_key)
        {
            for (auto &ch : routing_key)
            {
                if ((ch >= 'a' && ch <= 'z') ||
                    (ch >= 'A' && ch <= 'Z') ||
                    (ch >= '0' && ch <= '9') ||
                    (ch == '_' || ch == '.'))
                {
                    continue;
                }
                return false;
            }
            return true;
        }
        // a ~ z, A ~ Z, 0 ~ 9, _, ., #, *
        static bool isLegalBindingKey(const std::string &binding_key)
        {
            // 1.字符组成合法
            for (auto &ch : binding_key)
            {
                if ((ch >= 'a' && ch <= 'z') ||
                    (ch >= 'A' && ch <= 'Z') ||
                    (ch >= '0' && ch <= '9') ||
                    (ch == '_' || ch == '.') ||
                    (ch == '#' || ch == '*'))
                {
                    continue;
                }
                return false;
            }
            // 2.* 和 # 只能单独出现
            std::vector<std::string> sub_words;
            SplitHelper::split(binding_key, ".", sub_words);
            for (auto &word : sub_words)
            {
                if (word.size() > 1 &&
                    (word.find('*') != std::string::npos ||
                     word.find('#') != std::string::npos))
                {
                    return false;
                }
            }

            // * 和 #不能连续出现
            // 规则: # 左右两边不能出现通配符, 可以连续两个 *.*
            for (int i = 1; i < sub_words.size(); i++)
            {
                if (sub_words[i] == "*" && sub_words[i - 1] == "#")
                    return false;
                if (sub_words[i] == "#" && sub_words[i - 1] == "#")
                    return false;
                if (sub_words[i] == "#" && sub_words[i - 1] == "*")
                    return false;
            }
            return true;
        }
        // routing_key 和 binding_key进行匹配
        static bool route(ExchangeType type, const std::string &routing_key, const std::string &binding_key)
        {
            if (type == ExchangeType::DIRECT)
            {
                return (routing_key == binding_key);
            }
            if (type == ExchangeType::FANOUT)
            {
                return true;
            }
            // 规则匹配: 主题交换 news.# , news.music.pop
            // 1.将 routing_key 和 binding_key 进行分割存放到不同的vector中
            std::vector<std::string> bkeys, rkeys;
            int n_rkey = SplitHelper::split(routing_key, ".", rkeys);
            int n_bkey = SplitHelper::split(binding_key, ".", bkeys);
            // 2.定义并标记数组(动态规划) dp[0][0] = true;
            std::vector<std::vector<bool>> dp(n_bkey + 1, std::vector<bool>(n_rkey + 1, false));
            dp[0][0] = true;
            // 3.binding_key中以 # 起始 则将#对应行的第0列置为1.
            // for (int i = 1; i <= n_bkey; i++)
            // {
            //     if (bkeys[i - 1] == "#")
            //     {
            //         dp[i][0] = true;
            //         continue;
            //     }
            //     break;
            // }
            
            if(bkeys[0] == "#") dp[1][0] = true;

            // 4.使用routing_key中的每个单词与binding_key中的每个单词进行匹配并标记数组
            for (int i = 1; i <= n_bkey; i++)
            {
                for (int j = 1; j <= n_rkey; j++)
                {
                    // 如果当前bkey是个*，或者两个单词相同，表示单词匹配成功，则从左上方继承结果
                    if (bkeys[i - 1] == rkeys[j - 1] || bkeys[i - 1] == "*")
                    {
                        dp[i][j] = dp[i - 1][j - 1];
                    }
                    // 如果当前bkey是个#，则需要从左上，左边，上边继承结果
                    else if (bkeys[i - 1] == "#")
                    {
                        dp[i][j] = dp[i - 1][j - 1] | dp[i][j - 1] | dp[i - 1][j];
                    }
                }
            }
            return dp[n_bkey][n_rkey];
        }
    };
}